package com.lottery.game.common.utils;

import com.fasterxml.jackson.annotation.JsonProperty;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import lombok.extern.slf4j.Slf4j;


@Slf4j
public final class BeanUtil {
    public static final String JAVA_LANG_PACKAGE = "java.lang";

    private BeanUtil() {

    }

    /**
     * @param bean  Bean对象
     * @param name  属性名称
     * @param value 值
     * @author PengYanan
     */
    public static Object copyProperty(Object bean, String name, Object value) {
        try {
            BeanUtils.copyProperty(bean, name, value);
        } catch (IllegalAccessException e) {
            log.error("Bean对象反射时出现异常", e);
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            log.error("Bean对象中没有找到属性名为:{}", name, e);
        }
        return bean;
    }

    /**
     * 复制资源Bean中的属性值到目标Bean中
     *
     * @param targetObj 目标Bean
     * @param sourceObj 资源Bean
     */
    public static Object copyProperties(Object targetObj, Object sourceObj) {
        if (null == targetObj || null == sourceObj) {
            return targetObj;//增加null判断，防止后面copy时抛异常
        }
        try {
            BeanUtils.copyProperties(targetObj, sourceObj);
        } catch (Exception e) {
            log.error("bean CopyProperties Exception", e);
        }
        return targetObj;
    }

    public static <T>List<T> copyList(Class<T> targetClass,List<?> source){
        if(CollectionUtils.isEmpty(source)){
            return Collections.emptyList();
        }else{
            List<T> list=new ArrayList<T>();
            source.forEach(s->{
                try {
                    T t = targetClass.newInstance();
                    copyProperties(t,s);
                    list.add(t);
                } catch (Exception e) {
                    log.error("bean CopyProperties Exception", e);
                }

            });
            return list;
        }
    }

    /**
     * 取得属性值
     *
     * @param bean         Bean对象
     * @param propertyName 属性名
     * @return 属性值
     */
    public static Object getPropertyValue(Object bean, String propertyName) {
        if (bean instanceof Map) {
            return ((Map) bean).get(propertyName);
        } else {
            Class<?> clazz = bean.getClass();
            String methodName = "get" + StringUtil.firstCharUpperCase(propertyName);
            try {
                Method method = clazz.getMethod(methodName);
                return method.invoke(bean);
            } catch (Exception e) {
                log.error("无法找到属性对应的方法名", e);
                throw new RuntimeException("无法找到属性对应的方法名:" + methodName, e);
            }
        }
    }

    /**
     * 取得Key对应的所有值
     */
    @SuppressWarnings("unchecked")
    public static <T> List<T> getPropertyValues(List<?> list, String propertyName) {
        List<T> values = new ArrayList<T>(list.size());
        if (!list.isEmpty()) {
            Object bean0 = list.get(0);
            if (bean0 instanceof Map) {
                for (Object bean : list) {
                    T value = (T) ((Map) bean).get(propertyName);
                    values.add(value);
                }
            } else {
                try {
                    Class<?> clazz = list.get(0).getClass();
                    Method method = clazz.getMethod("get" + StringUtil.firstCharUpperCase(propertyName)
                    );
                    for (Object bean : list) {
                        T value = (T) method.invoke(bean);
                        values.add(value);
                    }
                } catch (Exception e) {
                    log.error("exception:", e);
                    throw new RuntimeException(e);
                }
            }
        }
        return values;
    }

    /**
     * 数据求和
     *
     * @param list       数据集
     * @param properties 求和的属性
     * @return 求和结果
     */
    public static BigDecimal[] sum(List<?> list, Object... properties) {
        BigDecimal[] numbers = new BigDecimal[properties.length];
        for (int i = 0; i < properties.length; i++) {
            numbers[i] = BigDecimal.valueOf(0);
        }
        List<Object[]> objectList = getPropertiesValues(list, properties);
        for (Object[] objects : objectList) {
            for (int i = 0; i < objects.length; i++) {
                if (numbers[i] == null) {
                    numbers[i] = toNumber(objects[i]);
                } else {
                    numbers[i] = numbers[i].add(toNumber(objects[i]));
                }
            }
        }
        return numbers;
    }

    /**
     * 数据求和
     *
     * @param list       数据集
     * @param properties 求平均值的属性
     * @return 求和结果
     */
    public static BigDecimal[] avg(List<?> list, Object... properties) {
        BigDecimal[] numbers = sum(list, properties);
        int total = list.size();
        for (int i = 0; i < numbers.length && total > 0; i++) {
            numbers[i] = numbers[i].divide(BigDecimal.valueOf(total), 4);
        }
        return numbers;
    }

    /**
     * 转换为数字
     *
     * @param value 对象
     * @return 数字
     */
    private static BigDecimal toNumber(Object value) {
        if (value == null || StringUtils.isBlank(value.toString())) {
            return new BigDecimal(0);
        } else if (value instanceof BigDecimal) {
            return (BigDecimal) value;
        } else if (value instanceof Number) {
            return new BigDecimal(value.toString());
        } else if (value instanceof String) {
            return new BigDecimal((String) value);
        }
        throw new RuntimeException("[" + value.getClass() + ";value=" + value + "]无效的数据类型...");
    }

    /**
     * 取得一些属性的值
     *
     * @param list       数据集
     * @param properties 属性名列表
     * @return 数据集
     */
    public static List<Object[]> getPropertiesValues(List<?> list, Object... properties) {
        if (list.isEmpty()) {
            return new ArrayList<>(0);
        }
        Object bean0 = list.get(0);
        List<Object[]> objectList = new ArrayList<Object[]>();
        if (bean0 instanceof Map) {// Map
            for (Object bean : list) {
                Map<?, ?> map = (Map<?, ?>) bean;
                Object[] objects = new Object[properties.length];
                for (int i = 0; i < properties.length; i++) {
                    Object o = map.get(properties[i]);
                    objects[i] = o;
                }
                objectList.add(objects);
            }
        } else if (bean0 instanceof Object[]) {// 对象数据
            for (Object bean : list) {
                Object[] source = (Object[]) bean;
                Object[] objects = new Object[properties.length];
                for (int i = 0; i < properties.length; i++) {
                    Object o = source[((Number) properties[i]).intValue()];
                    objects[i] = o;
                }
                objectList.add(objects);
            }
        } else if (bean0 instanceof Object) {// Object
            Class<?> clazz = list.get(0).getClass();
            try {
                for (Object bean : list) {
                    Object[] objects = new Object[properties.length];
                    for (int i = 0; i < properties.length; i++) {
                        String methodName = "get" + StringUtil.firstCharUpperCase(String.valueOf(properties[i]));
                        Method method = clazz.getMethod(methodName);
                        Object o = method.invoke(bean);
                        objects[i] = o;
                    }
                    objectList.add(objects);
                }
            } catch (Exception e) {
                log.error("exception:", e);
                throw new RuntimeException(e);
            }
        }
        return objectList;
    }

    private static Field[] getAllFields(Class clzz, Field[] fields) {
        if (clzz != Object.class) {
            Field[] newFields = ArrayUtils.addAll(fields, clzz.getDeclaredFields());
            return getAllFields(clzz.getSuperclass(), newFields);
        }
        return fields;
    }

    /**
     * Bean转换为Map格式
     */
    @SuppressWarnings("all")
    public static Map<String, Object> toMap(Object bean) {
        if (bean == null) {
            return null;
        }
        if (Map.class.isAssignableFrom(bean.getClass())) {
            return (Map<String, Object>) bean;
        }
        Field[] fields = getAllFields(bean.getClass(), new Field[0]);
        Map<String, Object> map = new LinkedHashMap<String, Object>();
        try {
            for (Field field : fields) {
                field.setAccessible(true);
                if (!Modifier.isStatic(field.getModifiers())) {
                    map.put(field.getName(), field.get(bean));
                }
            }
        } catch (Exception e) {
            log.error("exception:", e);
        }
        return map;
    }

    /**
     * Bean转换为Map格式 忽略NULL
     */
    @SuppressWarnings("all")
    public static Map<String, Object> toMapIgnoreNULL(Object bean) {
        if (bean == null) {
            return null;
        }
        if (Map.class.isAssignableFrom(bean.getClass())) {
            return (Map<String, Object>) bean;
        }
        Field[] fields = getAllFields(bean.getClass(), new Field[0]);
        Map<String, Object> map = new LinkedHashMap<String, Object>();
        try {
            for (Field field : fields) {
                field.setAccessible(true);
                if (!Modifier.isStatic(field.getModifiers()) && field.get(bean)!=null) {
                    map.put(field.getName(), field.get(bean));
                }
            }
        } catch (Exception e) {
            log.error("exception:", e);
        }
        return map;
    }

    /**
     * Bean转换为Map格式 忽略NULL
     */
    @SuppressWarnings("all")
    public static Map<String, String> toMapIgnoreNULLString(Object bean) {
        if (bean == null) {
            return null;
        }
        if (Map.class.isAssignableFrom(bean.getClass())) {
            return (Map<String, String>) bean;
        }
        Field[] fields = getAllFields(bean.getClass(), new Field[0]);
        Map<String, String> map = new LinkedHashMap<String, String>();
        try {
            for (Field field : fields) {
                field.setAccessible(true);
                if (!Modifier.isStatic(field.getModifiers()) && field.get(bean)!=null) {
                    map.put(field.getName(), field.get(bean).toString());
                }
            }
        } catch (Exception e) {
            log.error("exception:", e);
        }
        return map;
    }

    /**
     * 根据class中的属性名获取对应的json字段名，没有JsonProperty时，直接返回name
     * @param clazz
     * @param name
     * @return
     */
    public static String getJsonColumnName(Class clazz,String name) {
        Field field = BeanUtil.getFieldRecursion(clazz,name);
        if (null == field) {
            log.error("尝试获取json字段名出错,class={},feildName={}",clazz,name);
            return name;
        }
        return BeanUtil.getJsonColumnName(field,name);
    }

    /**
     * 根据属性Field获取对应的json字段名，没有JsonProperty时，直接返回fieldName
     * @param field
     * @param name
     * @return
     */
    public static String getJsonColumnName(Field field,String name) {
        if (null == field) {
            return name;
        }
        JsonProperty jsonProperty = field.getAnnotation(JsonProperty.class);
        if (null == jsonProperty) {
            return name;
        }
        String jsonName = jsonProperty.value();
        if (StringUtils.isBlank(jsonName)) {
            return name;
        }
        return jsonName;
    }

    /**
     * 获取class本身及所有父类定义的所有属性(不重复)
     * @param clazz
     * @return
     */
    public static Collection<Field> getAllDeclaredFields(Class clazz) {
        if (String.class == clazz) {
            return Collections.EMPTY_LIST;
        }
        Class cls = clazz;
        HashMap<String,Field> fieldMap = new HashMap<>();
        String key;
        while (Object.class != cls) {
            Field[] fields = cls.getDeclaredFields();
            for (Field field : fields) {
                key = field.getName();
                if (fieldMap.containsKey(key)) {
                    continue;
                }
                fieldMap.put(key,field);
            }
            cls = cls.getSuperclass();
        }
        return fieldMap.values();
    }
    /**
     * 根据名称递归获取class中定义的字段
     * @param clazz
     * @param name
     * @return
     */
    public static Field getFieldRecursion(Class clazz,String name) {
        if (Object.class == clazz || StringUtils.isBlank(name)) {
            return null;
        }
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if (name.equals(field.getName())) {
                return field;
            }
        }
        return BeanUtil.getFieldRecursion(clazz.getSuperclass(),name);
    }

    /**
     * 设置字符串值到对象的指定字段（会将字符串转换为目标字段的类型）
     * @param obj
     * @param field
     * @param value
     */
    public static void setFieldValue(Object obj,Field field,String value) {
        try {
            Object objValue = FormatUtils.stringParseBaseType(value, field.getType().getSimpleName());
            if (null == objValue) {
                return;
            }
            field.setAccessible(true);
            field.set(obj, objValue);
        } catch (Exception e) {
            log.error("设置数据失败，fieldName:{}，fieldValue:{}，{}",field.getName(),value, e.getLocalizedMessage());
        }
    }

    /**
     * 获取对象中指定属性的值，屏蔽异常
     * @param obj
     * @param field
     * @return Object
     */
    public static Object getFieldValue(Object obj,Field field) {
        Object value = null;
        try {
            field.setAccessible(true);
            value = field.get(obj);
        } catch (Exception e) {
            log.error("获取{}.{}的值出错,{}",obj.getClass(),field.getName(), e.getLocalizedMessage());
        }
        return value;
    }

    /**
     * 获取对象中指定属性的值，屏蔽异常
     * @param obj
     * @param fieldName
     * @return Object
     */
    public static Object getFieldValueByName(Object obj,String fieldName) {
        Field field = getFieldRecursion(obj.getClass(),fieldName);

        return getFieldValue(obj,field);
    }
}
